package com.xinsoft.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xinsoft.common.exception.BDException;
import com.xinsoft.constant.Const;
import com.xinsoft.entity.dto.ScmInventoryPlanDTO;
import com.xinsoft.entity.dto.ScmInventoryPlanParam;
import com.xinsoft.entity.po.*;
import com.xinsoft.entity.vo.ScmInventoryPlanPageVo;
import com.xinsoft.entity.vo.ScmInventoryPlanVo;
import com.xinsoft.mapper.ScmInventoryPlanItemMapper;
import com.xinsoft.mapper.ScmInventoryProfitInstockItemMapper;
import com.xinsoft.service.*;
import com.xinsoft.mapper.ScmInventoryPlanMapper;
import com.xinsoft.utils.TokenUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ZYM
 * @description 针对表【scm_inventory_plan(盘点方案)】的数据库操作Service实现
 * @createDate 2023-08-16 13:42:29
 */
@Service
public class ScmInventoryPlanServiceImpl extends ServiceImpl<ScmInventoryPlanMapper, ScmInventoryPlan>
        implements ScmInventoryPlanService {

    @Resource
    private ScmInventoryPlanItemMapper inventoryPlanItemMapper;
    @Resource
    private ScmInventoryProfitInstockItemMapper inventoryProfitInstockItemMapper;
    @Autowired
    private ScmInventoryPlanItemService inventoryPlanItemService;
    @Autowired
    private ScmInventoryProfitInstockService inventoryProfitInstockService;
    @Autowired
    private ScmInventoryProfitInstockItemService inventoryProfitInstockItemService;
    @Autowired
    private ScmInventoryLossOutstockService inventoryLossOutstockService;
    @Autowired
    private ScmInventoryLossOutstockItemService inventoryLossOutstockItemService;
    @Autowired
    private ScmInventoryProfitLossService inventoryProfitLossService;
    @Autowired
    private SysDocumentNumService sysDocumentNumService;
    @Autowired
    private ScmStockService stockService;


    @Override
    public IPage<ScmInventoryPlanPageVo> findPageListByParam(ScmInventoryPlanParam param) {
        Page<ScmInventoryPlanPageVo> page = new Page<>(param.getCurrentPage(), param.getPageSize());
        return baseMapper.findListByParam(page, param);
    }

    @Override
    public ScmInventoryPlanVo detail(Integer id) {
        ScmInventoryPlanVo vo = baseMapper.getById(id);
        // // 未执行完毕更新盘点详情的系统数量（防止盘点未结束之前库存发生变动导致盘点不准确）
        // if (!Objects.equals(vo.getCheckStatus(), Const.CheckStatus.EXECUTION_IS_COMPLETE)) {
        //     this.updateBatchInventoryPlanItem(id);
        // }
        if (Objects.nonNull(vo)) {
            vo.setScmInventoryPlanItemList(inventoryPlanItemMapper.listByInventoryPlanId(id));
        }
        return vo;
    }

    public void updateBatchInventoryPlanItem(Integer id) {
        List<ScmInventoryPlanItem> planItems = inventoryPlanItemService.list(Wrappers.lambdaQuery(ScmInventoryPlanItem.class).eq(ScmInventoryPlanItem::getInventoryPlanId, id));
        List<Integer> stockIds = planItems.stream().map(ScmInventoryPlanItem::getStockId).distinct().collect(Collectors.toList());
        List<ScmStock> scmStocks = stockService.listByIds(stockIds);
        Map<Integer, ScmStock> stockMap = scmStocks.stream().collect(Collectors.toMap(ScmStock::getId, stock -> stock));
        planItems.forEach(item -> {
            ScmStock scmStock = stockMap.get(item.getStockId());
            if (Objects.nonNull(scmStock)) {
                item.setSystemCount(scmStock.getBatchNum());
                item.setSystemCountSupport(scmStock.getBatchAssistNum());
            }
        });
        inventoryPlanItemService.updateBatchById(planItems);
    }

    @Override
    public Integer tempSave(ScmInventoryPlanDTO dto) {
        ScmInventoryPlan inventoryPlan = new ScmInventoryPlanDTO();
        BeanUtils.copyProperties(dto, inventoryPlan);
        inventoryPlan.setCheckStatus(Const.CheckStatus.NOT_EXECUTED);

        List<ScmInventoryPlanItem> itemList = dto.getScmInventoryPlanItemList();
        if (Objects.isNull(inventoryPlan.getId())) {
            // 更新流水号
            sysDocumentNumService.incrementLastSerialByDocumentId(Const.DOCUMENT_ID.INVENTORY_PLAN_DOCUMENT);
            this.save(inventoryPlan);
        } else {
            inventoryPlan.setCheckStatus(Const.CheckStatus.EXECUTING);
            this.updateById(inventoryPlan);
        }

        // 保存盘点结果明细
        itemList.forEach(item -> item.setInventoryPlanId(inventoryPlan.getId()));
        inventoryPlanItemService.saveOrUpdateBatch(itemList);
        return inventoryPlan.getId();
    }

    @Override
    public void over(Integer id) {
        ScmInventoryPlan inventoryPlan = this.getById(id);
        if (inventoryPlan.getCheckStatus().equals(Const.CheckStatus.EXECUTION_IS_COMPLETE)) {
            throw new BDException("已结束状态，请勿重复操作！");
        }
        int count = inventoryPlanItemService.count(Wrappers.lambdaQuery(ScmInventoryPlanItem.class)
                .eq(ScmInventoryPlanItem::getInventoryPlanId, id)
                .isNull(ScmInventoryPlanItem::getCheckCount));
        if (count > 0) {
            throw new BDException("盘点方案存在未盘点明细行，结束失败！");
        }
        inventoryPlan.setCheckStatus(Const.CheckStatus.EXECUTION_IS_COMPLETE);
        this.updateById(inventoryPlan);
    }

    @Override
    public void del(List<Integer> ids) {
        List<ScmInventoryPlan> inventoryPlanList = this.list(Wrappers.lambdaQuery(ScmInventoryPlan.class)
                .in(ScmInventoryPlan::getId, ids)
                .eq(ScmInventoryPlan::getCheckStatus, Const.CheckStatus.NOT_EXECUTED));
        if (ids.size() > inventoryPlanList.size()) {
            throw new BDException("执行中或执行完毕的盘点方案不允许删除！");
        }
        inventoryPlanItemService.remove(Wrappers.lambdaQuery(ScmInventoryPlanItem.class).in(ScmInventoryPlanItem::getInventoryPlanId, ids));
        this.removeByIds(ids);
    }

    @Override
    public void generateInventoryProfit(Integer id) {
        ScmInventoryPlan inventoryPlan = this.getById(id);
        if (!inventoryPlan.getCheckStatus().equals(Const.CheckStatus.EXECUTION_IS_COMPLETE)) {
            throw new BDException("盘点方案未结束，无法生成下游单据！");
        }
        // 是否有盘盈（rows>0 是）
        int count = inventoryPlanItemService.count(Wrappers.lambdaQuery(ScmInventoryPlanItem.class)
                .eq(ScmInventoryPlanItem::getInventoryPlanId, id)
                .gt(true, ScmInventoryPlanItem::getProfitLossCount, 0));

        // 是否生成盘盈单（count2>0 是）
        int count2 = inventoryProfitLossService.count(Wrappers.lambdaQuery(ScmInventoryProfitLoss.class)
                .eq(ScmInventoryProfitLoss::getHeadId, id)
                .eq(ScmInventoryProfitLoss::getDocumentTypeId, Const.DOCUMENT_ID.INVENTORY_PROFIT_DOCUMENT));
        // 是否生成盘盈入库（count3>0 是）
        Map<String, Object> param = new HashMap<>();
        param.put("headId", id);
        param.put("documentTypeId", Const.DOCUMENT_ID.INVENTORY_PROFIT_DOCUMENT);
        int count3 = inventoryProfitInstockItemMapper.countByParam(param);

        // 盘盈数据
        List<ScmInventoryPlanItem> profitList = inventoryPlanItemService.list(Wrappers.lambdaQuery(ScmInventoryPlanItem.class)
                .eq(ScmInventoryPlanItem::getInventoryPlanId, id)
                .gt(true, ScmInventoryPlanItem::getProfitLossCount, 0));

        if (count > 0 && count2 == 0 && count3 == 0) {
            String code = sysDocumentNumService.getSysDocumentNumByDocumentId(Const.DOCUMENT_ID.INVENTORY_PROFIT_DOCUMENT);
            // 1.保存盘点盈亏单
            this.saveProfitLoss(Const.DOCUMENT_ID.INVENTORY_PROFIT_DOCUMENT, code, profitList);

            // 2.保存盘盈入库
            ScmInventoryProfitInstock instock = this.saveInventoryProfitInstock(inventoryPlan, profitList);
            // 3.审核并保存库存变动记录和库存数据
            inventoryProfitInstockService.audit(instock.getId());
        } else if (count > 0 && count2 > 0 && count3 == 0) {
            // 生成盘盈入库
            ScmInventoryProfitInstock instock = this.saveInventoryProfitInstock(inventoryPlan, profitList);
            // 审核并保存库存变动记录和库存数据
            inventoryProfitInstockService.audit(instock.getId());
        } else if (count > 0 && count3 > 0) {
            throw new BDException("盘盈数据已生成盘盈入库，无需再次生成！");
        } else if (count == 0) {
            throw new BDException("此次盘点无盘盈！");
        }


    }

    @Override
    public void generateInventoryLoss(Integer id) {
        ScmInventoryPlan inventoryPlan = this.getById(id);
        if (!inventoryPlan.getCheckStatus().equals(Const.CheckStatus.EXECUTION_IS_COMPLETE)) {
            throw new BDException("盘点方案未结束，无法生成下游单据！");
        }
        // 是否有盘亏（rows>0 是）
        int count = inventoryPlanItemService.count(Wrappers.lambdaQuery(ScmInventoryPlanItem.class)
                .eq(ScmInventoryPlanItem::getInventoryPlanId, id)
                .lt(true, ScmInventoryPlanItem::getProfitLossCount, 0));

        // 是否生成盘盈亏（count2>0 是）
        int count2 = inventoryProfitLossService.count(Wrappers.lambdaQuery(ScmInventoryProfitLoss.class)
                .eq(ScmInventoryProfitLoss::getHeadId, id)
                .eq(ScmInventoryProfitLoss::getDocumentTypeId, Const.DOCUMENT_ID.INVENTORY_LOSS_DOCUMENT));
        // 是否生成盘亏出库（count3>0 是）
        Map<String, Object> param = new HashMap<>();
        param.put("headId", id);
        param.put("documentTypeId", Const.DOCUMENT_ID.INVENTORY_LOSS_DOCUMENT);
        int count3 = inventoryProfitInstockItemMapper.countByParam(param);

        // 盘亏数据
        List<ScmInventoryPlanItem> lossList = inventoryPlanItemService.list(Wrappers.lambdaQuery(ScmInventoryPlanItem.class)
                .eq(ScmInventoryPlanItem::getInventoryPlanId, id)
                .lt(true, ScmInventoryPlanItem::getProfitLossCount, 0));

        if (count > 0 && count2 == 0 && count3 == 0) {
            String code = sysDocumentNumService.getSysDocumentNumByDocumentId(Const.DOCUMENT_ID.INVENTORY_LOSS_DOCUMENT);
            // 1.保存盘点盈亏单
            this.saveProfitLoss(Const.DOCUMENT_ID.INVENTORY_LOSS_DOCUMENT, code, lossList);

            // 2.保存盘亏出库
            ScmInventoryLossOutstock outstock = this.saveInventoryLossOutstock(inventoryPlan, lossList);
            // 3.审核并保存库存变动记录和库存数据
            inventoryLossOutstockService.audit(outstock.getId());
        } else if (count > 0 && count2 > 0 && count3 == 0) {
            // 生成盘亏出库
            ScmInventoryLossOutstock outstock = this.saveInventoryLossOutstock(inventoryPlan, lossList);
            // 3.审核并保存库存变动记录和库存数据
            inventoryLossOutstockService.audit(outstock.getId());
        } else if (count > 0 && count3 > 0) {
            throw new BDException("盘亏数据已生成盘亏出库，无需再次生成！");
        } else if (count == 0) {
            throw new BDException("此次盘点无盘亏！");
        }
    }

    @Override
    public void scanQrInventory(ScmInventoryPlanDTO dto) {
        List<ScmInventoryPlanItem> itemList = dto.getScmInventoryPlanItemList();
        Map<String, ScmInventoryPlanItem> planItemMap = itemList.stream().collect(Collectors.toMap(item -> String.join("-", item.getMaterialId().toString(), item.getWarehouseId().toString(), item.getWarehItemId().toString()), item -> item));
        ScmInventoryPlan inventoryPlan = this.getById(dto.getId());
        List<ScmInventoryPlanItem> planItemList = inventoryPlanItemService.list(Wrappers.lambdaQuery(ScmInventoryPlanItem.class)
                .eq(ScmInventoryPlanItem::getInventoryPlanId, dto.getId()));
        if (Objects.isNull(inventoryPlan)) {
            throw new BDException("请传入正确的盘点方案id");
        }
        if (Objects.equals(inventoryPlan.getCheckStatus(), Const.CheckStatus.EXECUTION_IS_COMPLETE)) {
            throw new BDException("此盘点已执行结束");
        }
        planItemList.forEach(item -> {
            String key = String.join("-", item.getMaterialId().toString(), item.getWarehouseId().toString(), item.getWarehItemId().toString());
            String qrId = planItemMap.get(key).getQrId();
            if (planItemMap.containsKey(key) && Objects.equals(planItemMap.get(key).getBatchNo(), item.getBatchNo())) {
                // if (StringUtils.isEmpty(item.getQrIdCount())){
                //     // 首次盘点
                BigDecimal qrCheckCount = planItemMap.get(key).getCheckCount();
                Map<String, Object> map = new HashMap<>();
                map.put(qrId, qrCheckCount);
                item.setCheckCount(qrCheckCount);
                item.setCheckCountSupport(item.getCheckCount().multiply(item.getConvCoefficient()));
                item.setProfitLossCount(item.getSystemCount().subtract(item.getCheckCount()));
                item.setProfitLossCountSupport(item.getProfitLossCount().multiply(item.getConvCoefficient()));
                item.setQrIdCount(JSON.toJSONString(map));
                // }else {
                //     Map map = JSON.parseObject(item.getQrIdCount(), Map.class);
                //     if (map.containsKey(qrId)){
                //
                //     }
                // }
            }
        });
        // 更新盘点方案明细
        inventoryPlanItemService.saveOrUpdateBatch(planItemList);
        // 标记方案为扫码盘点
        inventoryPlan.setQrSign(1);
        this.updateById(inventoryPlan);
    }

    /**
     * 保存盘点盈亏单
     *
     * @param documentTypeId 源单类型id
     * @param code           盘盈/盘亏编码
     * @param itemList       盘盈/盘亏数据
     */
    public void saveProfitLoss(Integer documentTypeId, String code, List<ScmInventoryPlanItem> itemList) {
        List<ScmInventoryProfitLoss> profitLossList = new ArrayList<>();
        itemList.forEach(item -> {
            ScmInventoryProfitLoss profitLoss = new ScmInventoryProfitLoss();
            profitLoss.setCode(code);
            profitLoss.setHeadId(item.getInventoryPlanId());
            profitLoss.setBodyId(item.getId());
            profitLoss.setDocumentTypeId(documentTypeId);
            profitLossList.add(profitLoss);
        });
        inventoryProfitLossService.saveBatch(profitLossList);
    }

    /**
     * 保存盘盈入库
     *
     * @param inventoryPlan 盘点方案
     * @param profitList    盘盈数据
     */
    public ScmInventoryProfitInstock saveInventoryProfitInstock(ScmInventoryPlan inventoryPlan, List<ScmInventoryPlanItem> profitList) {
        ScmInventoryProfitInstock instock = new ScmInventoryProfitInstock();
        instock.setInstockCode(sysDocumentNumService.getSysDocumentNumByDocumentId(Const.DOCUMENT_ID.INVENTORY_PROFIT_INSTOCK_DOCUMENT));
        instock.setInstockPersonId(TokenUtil.getUserId());
        instock.setInstockDate(new Date());
        instock.setAuditStatus(Const.AUDIT_STATUS_WAIT);
        inventoryProfitInstockService.save(instock);
        sysDocumentNumService.incrementLastSerialByDocumentId(Const.DOCUMENT_ID.INVENTORY_PROFIT_INSTOCK_DOCUMENT);
        List<ScmInventoryProfitInstockItem> instockItemList = new ArrayList<>();
        profitList.forEach(item -> {
            ScmInventoryProfitInstockItem instockItem = new ScmInventoryProfitInstockItem();
            instockItem.setProfitInstockId(instock.getId());
            instockItem.setMaterialId(item.getMaterialId());
            instockItem.setInstockNum(item.getProfitLossCount().abs());
            instockItem.setAssistUom(item.getSupportUom());
            instockItem.setInstockassistNum(item.getProfitLossCountSupport().abs());
            instockItem.setConvCoefficient(item.getConvCoefficient());
            instockItem.setBatch(item.getBatchNo());
            instockItem.setWarehouseId(item.getWarehouseId());
            instockItem.setWarehItemId(item.getWarehItemId());

            instockItem.setSourceId(inventoryPlan.getId());
            instockItem.setSourceType(Const.DOCUMENT_ID.INVENTORY_PLAN_DOCUMENT);
            instockItem.setSourceCode(inventoryPlan.getCode());
            instockItemList.add(instockItem);
        });
        inventoryProfitInstockItemService.saveBatch(instockItemList);

        return instock;
    }

    /**
     * 保存盘亏出库
     *
     * @param inventoryPlan 盘点方案
     * @param lossList      盘亏数据
     */
    public ScmInventoryLossOutstock saveInventoryLossOutstock(ScmInventoryPlan inventoryPlan, List<ScmInventoryPlanItem> lossList) {
        ScmInventoryLossOutstock outstock = new ScmInventoryLossOutstock();
        outstock.setOutstockCode(sysDocumentNumService.getSysDocumentNumByDocumentId(Const.DOCUMENT_ID.INVENTORY_LOSS_OUTSTOCK_DOCUMENT));
        outstock.setOutstockPersonId(TokenUtil.getUserId());
        outstock.setOutstockTime(new Date());
        outstock.setAuditStatus(Const.AUDIT_STATUS_WAIT);
        inventoryLossOutstockService.save(outstock);
        sysDocumentNumService.incrementLastSerialByDocumentId(Const.DOCUMENT_ID.INVENTORY_LOSS_OUTSTOCK_DOCUMENT);
        List<ScmInventoryLossOutstockItem> outstockItemList = new ArrayList<>();
        lossList.forEach(item -> {
            ScmInventoryLossOutstockItem outstockItem = new ScmInventoryLossOutstockItem();
            outstockItem.setOutstockId(outstock.getId());
            outstockItem.setMaterialId(item.getMaterialId());
            outstockItem.setOutstockNum(item.getProfitLossCount().abs());
            outstockItem.setAssistUom(item.getSupportUom());
            // outstockItem.setInstockassistNum(item.getProfitLossCountSupport().abs());
            outstockItem.setConvCoefficient(item.getConvCoefficient());
            outstockItem.setBatch(item.getBatchNo());
            outstockItem.setWarehouseId(item.getWarehouseId());
            outstockItem.setWarehItemId(item.getWarehItemId());

            outstockItem.setSourceId(inventoryPlan.getId());
            outstockItem.setSourceType(Const.DOCUMENT_ID.INVENTORY_PLAN_DOCUMENT);
            outstockItem.setSourceCode(inventoryPlan.getCode());
            outstockItemList.add(outstockItem);
        });
        inventoryLossOutstockItemService.saveBatch(outstockItemList);

        return outstock;
    }

}




